﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Configuration;
using System.Text.RegularExpressions;
using nGSO.Utils;
using nGSO.Model;
using nGSO.KnapsackProblem;

namespace nGSO.Performance
{
    public class PerfManager
    {
        //http://www.math.oregonstate.edu/home/programs/undergrad/CalculusQuestStudyGuides/vcalc/min_max/min_max.html

        public PerfTests StartTest(GsoConfig config)
        {
            PerfTests test = new PerfTests();

            test.GsoVer = GetGsoVersion(config.AfterAgentMoveExtensions);
            test.CreateDate = DateTime.Now;
            test.Population = config.Population;
            test.MaxIterations = config.MaxIterations;
            test.LucifierinEnhancement = (float)config.LuciferinEnhancement;
            test.BetaConst = (float)config.BetaConst;
            test.FunctionKey = config.SelectedOptimizationTask.Name;
            test.RadialSensorRange = (float)config.RadialSensorRange;
            test.SpaceSize = config.SpaceSize;
            test.StepSize = (float)config.StepSize;
            test.UseRandomizeEnhancement = config.UseRandomizeSelector;
            test.DesiredNoOfNeighbors = config.DesiredNoOfNeighbors;
            test.DefaultLuminescence = config.DefaultLuminescence;
            test.Dims = config.Dims;
            test.Decay = (float)config.Decay;
            test.PerfKpResultId = null;

            using (DataModelEntities ctx = PerfDatabase.GetContext())
            {
                ctx.PerfTests.AddObject(test);
                ctx.SaveChanges();
            }

            return test;
        }
        
        private string GetGsoVersion(List<BaseAfterAgentMoveExtension> list)
        {
            if (list.Any(s => s.Name == "ExtendedAgentJumpIfNoNeighbours" && s.Enabled))
            {
                if (list.Any(s => s.Name == "NeighborhoodRangeExtender" && s.Enabled))
                {
                    return "gsov2";
                }
                else 
                {
                    return "gsov1";
                }
            }
            return "normal";
        }

        public void AddResult(PerfTests perfEntry, SearchSpace result, GsoConfig config)
        {
            using (DataModelEntities ctx = PerfDatabase.GetContext())
            {
                ctx.PerfTests.Attach(perfEntry);

                perfEntry.EndDate = DateTime.Now;
                ctx.SaveChanges();

                EffecinityCalc calc = new EffecinityCalc(config);

                IEnumerable<double[]> locs = result.Agents.Select(s => s.Loc);
                
                PeakAnalyzeData peakData = calc.CountPeaks(locs);
                perfEntry.AssertPeaks = peakData.TotalPeaks;
                perfEntry.DetectedPeaks = peakData.DetectedPeaks;
                perfEntry.OverallEfficiency = (float)peakData.Efficiency;
                ctx.SaveChanges();

                for (int i = 0; i < config.MaxIterations; i++)
                {
                    List<AgentHistory> historyAt = new List<AgentHistory>();

                    foreach (Agent agent in result.Agents)
                    {
                        historyAt.Add(agent.History[i]);
                    }

                    IEnumerable<double[]> locsAtIteration = historyAt.Select(s => s.Loc);
                    PeakAnalyzeData peakDataAtIteration = calc.CountPeaks(locsAtIteration);

                    PerfEntryHistory peh = new PerfEntryHistory();

                    peh.Effecinity = peh.Effecinity;
                    peh.Iteration = i;
                    peh.Peaks = peakDataAtIteration.DetectedPeaks;
                    peh.Effecinity = (float)peakDataAtIteration.Efficiency;
                    peh.PerfTestId = perfEntry.PerfTestId;

                    ctx.AddToPerfEntryHistory(peh);
                }
                
                    ctx.SaveChanges();
                
                
            }
        }

        public void AddKpResult(PerfTests test, SearchSpace gsoResult, KnapsackProblem.KpConfigElement KPConf, int testAttempt)
        {
            KpSolutionTester solutionTester = new KpSolutionTester();
            var kpResults = solutionTester.GetResult(gsoResult, KPConf);

            using (DataModelEntities ctx = PerfDatabase.GetContext())
            {
                ctx.Attach(test);
                
                int solutionId = 0;

                foreach (KpResult kpRes in kpResults)
                {
                    double costFactor = ((double)kpRes.TotalCost / KPConf.OptimizedCost) * 100;
                    double weightFactor = ((double)kpRes.TotalWeight / KPConf.MaxCapacity) * 100;
                    double avgEffecinity = ((double)costFactor + weightFactor) / 2 /100;

                    if (avgEffecinity > 1)
                        avgEffecinity = 1 / avgEffecinity;
                    

                    PerfKp kp = new PerfKp()
                    {
                        Cost = kpRes.TotalCost,
                        Weight = kpRes.TotalWeight,
                        IsOptimum = kpRes.Ok,
                        SolutionId = solutionId++,
                        TestAttempt = testAttempt,
                        PerfTestId = test.PerfTestId,
                        Effecinity = (float)avgEffecinity,
                        EncodedSolution = kpRes.SelectedObjects,
                        Neighbours = kpRes.Neighbours
                    };
                    ctx.PerfKp.AddObject(kp);
                }

                ctx.SaveChanges();
            }
        }

        public void UpdateOverallKPEffcienty(PerfTests test, KpConfigElement kpConf)
        {
            using (DataModelEntities ctx = PerfDatabase.GetContext())
            {
                ctx.Attach(test);
                test.EndDate = DateTime.Now;
                ctx.SaveChanges();

                var testSolutions = ctx.PerfKp.Where(s=>s.PerfTestId == test.PerfTestId);

                PerfKpResult kpResult = new PerfKpResult();
                var optimalSolutions = testSolutions.Where(s => s.IsOptimum);

                var firstSolutionInTest = optimalSolutions.OrderBy(s=>s.TestAttempt).FirstOrDefault();
                kpResult.CorrectSolutions = optimalSolutions.Count();
                kpResult.FirstSolutionTestIndx = (firstSolutionInTest != null) ? firstSolutionInTest.TestAttempt : 0;
                kpResult.NumberOfTests = kpConf.TestCount;
                kpResult.PerfKpBest = ctx.PerfKp.Where(s => s.Weight <= kpConf.MaxCapacity).OrderByDescending(z => z.Cost).First().SolutionId;
                // number of tests whete found one or more correct solutions
                kpResult.SuccessTests = testSolutions.GroupBy(s => s.TestAttempt).Where(s => s.Any(z => z.IsOptimum)).Count();
                kpResult.TotalSolutions = testSolutions.Count();
                kpResult.Costs = kpConf.EncodedCosts;
                kpResult.Weights = kpConf.EncodedWeights;
                kpResult.DesiredCost = kpConf.OptimizedCost;
                kpResult.MaxCapacity = kpConf.MaxCapacity;
                ctx.PerfKpResult.AddObject(kpResult);
                ctx.SaveChanges();

                test.PerfKpResultId = kpResult.PerfKpResultId;
                ctx.SaveChanges();
            }
        }

        public void Delete(PerfTests perfEntry)
        {
            using (DataModelEntities ctx = PerfDatabase.GetContext())
            {
                ctx.Attach(perfEntry);
                ctx.PerfTests.DeleteObject(perfEntry);
                ctx.SaveChanges();
            }
        }
    }
}